July 24 2019

Agenda


Animation: When and Why?
Introducing the Tools
Hands-On Tutorial

Follow Along!

https://github.com/skirmer/animating_dataviz

So You’re Thinking About Animating

What are some reasons to animate a plot?

Bad Reasons

  • It’s neat
  • I want to show off
  • One of my dimensions is time

Good Reasons

  • I have four or more meaningful data dimensions
  • I want to display many levels of a single dimension

Also: If you need to see all the data points at the same time DON’T use animation

Use Case Ideas


Thomas Lin Pedersen’s example from https://gganimate.com - (@thomasp85)

  • Data in meaningful groups changing over long periods of time
  • What does this plot actually tell the viewer?

More Good Examples

Multiple animations

GIS

Forecasting


Shadowing

Model Performance

Groups Over Time with Reveal

How to Think About the Design

  • What information do you want the audience to take away?
  • Movement leads the viewer to interpretation of the data - this can be good or bad
  • Speed creates meaning, be conscious of that
  • Each static frame should be informative alone as well

(Be open to the idea that you might not need animation to be most effective)

The Tools

Key Packages to Get

library(ggplot2) # make plots
library(gganimate) # animate the plots
library(gifski) # render gifs

Optional, maybe interesting packages

library(transformr) # additional smooth transformations
library(patchwork) # arrange multiple plots in a single layout
library(ggforce) # extended options for ggplot functionality

Key Concepts

Frame

  • Animations are a series of single images (frames) strung together (think of “frames per second”)

State

  • The current grouping variable being shown. It may be one frame, or a single state representation can stay visible for multiple frames

Transition

  • How the visual rendering moves from state to state

Enter/Exit

  • How datapoints with no predecessor or follower are handled

Tutorial

Project Overview

Using data on displaced persons living in South Africa, show patterns and trends in refugee movement over twenty-plus years.

  1. Where do most refugees come from?

    • These may be places with high instability or political turmoil at a given time
  2. Over time, are the most common countries of origin about the same?
  3. Is the number of refugees coming from the most common countries of origin stable?

Follow along with the notebook: https://github.com/skirmer/animating_dataviz/blob/master/ioslides_deck.Rmd

Data Source: UNHCR via data.world: UNHCR’s populations of concern residing in South Africa

Load Data

Grab dataset of displaced persons living in South Africa

X_date_year X_country_residence X_country_origin X_population_type X_affected
1996 South Africa Angola Refugees (incl. refugee-like situations) 3876
1996 South Africa Angola Returned refugees 308
1996 South Africa Bangladesh Refugees (incl. refugee-like situations) 452
1996 South Africa China Refugees (incl. refugee-like situations) 469

Mutate Data

Select the top 10 countries of origin for each year, apply some filters.

library(magrittr)
library(dplyr)

plotDT <- dtset %>%
  filter(X_country_origin != "Various/Unknown") %>%
  filter(X_population_type == "Refugees (incl. refugee-like situations)") %>%
  group_by(X_date_year) %>%
  mutate(rank = rank(-X_affected, ties.method = "first") * 1) %>%
  ungroup() %>%
  filter(rank <= 10) %>%
  data.frame()

Our New Data

X_date_year X_country_residence X_country_origin X_population_type X_affected rank
1996 South Africa Angola Refugees (incl. refugee-like situations) 3876 1
1996 South Africa Bangladesh Refugees (incl. refugee-like situations) 452 9
1996 South Africa China Refugees (incl. refugee-like situations) 469 8
1996 South Africa Dem. Rep. of the Congo Refugees (incl. refugee-like situations) 2505 3

Grouped Bar Plot - Code

Group by country, bar height is affected persons

baseplot1 <- ggplot(plotDT, 
aes(X_date_year, 
    group = X_country_origin, 
    fill = as.factor(X_country_origin), 
    color = as.factor(X_country_origin)))+
theme_bw()+
theme(legend.position = "bottom")+
geom_bar(aes(y = X_affected), stat = "identity", position = "dodge")

Grouped Bar Plot - Rendered

Ew. This is not effective.

What if we try to make a race plot instead?

Let’s make this!

Adjustment 1: Flip Axis

baseplot2 <- baseplot1 +
  coord_flip(clip = "off", expand = FALSE, ylim = c(0, 50000))

Flipped Axis - Render

Adjustment 2: Stop the bar grouping (dodge), switch axis to country from year

Can’t use the same stub now.

baseplot3 <- ggplot(plotDT, 
  aes(x=rank, 
    group = X_country_origin, 
    fill = as.factor(X_country_origin), 
    color = as.factor(X_country_origin)))+
theme_bw()+
theme(legend.position = "bottom")+
geom_text(aes(y = 0, label = paste(X_country_origin, " ")), vjust = 0.2, hjust = 1) +
coord_flip(clip = "off", expand = FALSE, ylim = c(0, 50000)) +
  geom_bar(aes(y = X_affected), stat = "identity", position = "identity")

Stop Grouping - Render

Check in: baseplot2 vs baseplot3

Left side: grouped bar, sorted by year
Right side: not grouped bar, sorted by rank

Adjustment 3: Adjust margins, fix axis text, drop legend

If your stub has a theme() segment, applying a new one will overrule it.

baseplot3 <- baseplot3 + 
  theme(legend.position = "none",
    axis.ticks.y = element_blank(), 
    axis.text.y  = element_blank(), 
    axis.title.y = element_blank(),
    plot.margin = margin(1,1,1,5, "cm"))

Drop Legend - Render

Adjustment 4: Prettify Y, reverse X direction

baseplot3 <- baseplot3 +
scale_y_continuous(labels = scales::comma) +
scale_x_reverse()

Reverse X - Render

Now we have the different “frames” all layered on top of each other.

Decision Point: How to Proceed?

Before continuing - are we SURE animation is our best option?

Revisit our goals:

  1. Where do most refugees come from?
  2. Over time, are the most common countries of origin about the same?
  3. Is the number of refugees coming from the most common countries of origin stable?

Let’s try static options one last time just to be sure.

Faceting?

No.

Faceting?

Definitely not.

Develop the Animation

Basic Version

Literally add one more line of code to your ggplot object.

animp <- baseplot3 +
  transition_states(X_date_year)

Basic Animation - Render

It’s nice, but we can do better

  1. Viewer can’t tell what the frames represent
  2. Y axis needs labeling, bars need clear labels
  3. It’s moving abruptly between frames

Add Dynamic Labels

Solving Problem 1 and 2: Added a descriptive title/label that indicate the year of the frame, label bars

animp <- baseplot3 +
  geom_text(aes(y = X_affected, 
    label = as.character(X_affected)), 
    color = "black", vjust = 0.2, hjust = -.5)+
  labs(title = "Refugees Residing in South Africa by Origin, {closest_state}"
    , y="Affected Persons")+
  transition_states(X_date_year)

Dynamic Labels - Render

Transition Options: Exit/Enter

Solving Problem 3: how do we want the animation elements to move?

Option: shrink and grow on exit and enter

animp <- baseplot3 +
  geom_text(aes(y = X_affected, 
    label = as.character(X_affected)), 
    color = "black", vjust = 0.2, hjust = -.5)+
  labs(title = "Refugees Residing in South Africa by Origin, {closest_state}"
    , y="Affected Persons")+
  transition_states(X_date_year)+
  enter_grow() +
  exit_shrink()

Exit/Enter - Render

It’s interesting, but probably not serving the project objectives

Transition Options: Easing

Solving Problem 3: how do we want the animation elements to move?

Option: Ease between positions (moving on page, not exiting or entering)

animp <- baseplot3 +
  geom_text(aes(y = X_affected, 
    label = as.character(X_affected)), 
    color = "black", vjust = 0.2, hjust = -.5)+
  labs(title = "Refugees Residing in South Africa by Origin, {closest_state}"
  , y="Affected Persons")+
  transition_states(X_date_year)+
  ease_aes('quartic-in-out')

Easing Transitions - Render

Makes the transition speed change as it moves (functions may be cubic, quartic, etc)

Transition Options: More Energetic

Solving Problem 3: how do we want the animation elements to move?

Option: For fun, let’s try “back” to see a springier approach

animp <- baseplot3 +
  geom_text(aes(y = X_affected, 
    label = as.character(X_affected)), 
    color = "black", vjust = 0.2, hjust = -.5)+
  labs(title = "Refugees Residing in South Africa by Origin, {closest_state}"
  , y="Affected Persons")+
  transition_states(X_date_year)+
  ease_aes('back-in-out')

Bouncy Transitions - Render

Feels a little cartoony- interesting, but again perhaps not what we need

Transition Options: Timing

Solving Problem 3: how do we want the animation elements to move?

In addition to entry, exit, and transition easing:

  • Set pace for the states/transitions
  • Ensure no smoothing between the end and restarting
animp <- baseplot3 +
  geom_text(aes(y = X_affected, 
    label = as.character(X_affected)), 
    color = "black", vjust = 0.2, hjust = -.5)+
  labs(title = "Refugees Residing in South Africa by Origin, {closest_state}"
    , y="Affected Persons")+
  transition_states(X_date_year,transition_length = 5, 
      state_length = c(rep(.25, 21), 20), wrap = FALSE)+
  ease_aes('linear')+
  enter_fade() +
  exit_fade()

Set Timings - Render

Slower pace feels smoother, and doesn’t insinuate that the last frame and the first flow into each other

Transition States - What Does It Do?

  transition_states(
    X_date_year,
    transition_length = 5, 
    state_length = c(rep(.25, 21), 20),
    wrap = FALSE)+

Assign the frame unit - here we use the year.

Transition length

  • period of time we use for change from frame to frame
  • slow it down if you want a smooth looking animation

State length

  • period of time where the frame stays static
  • here I am making the very last frame stay static longer

Wrap determines whether to apply transition smoothing between the end and restarting
Experiment with these settings to get the look you want!

Render the Final Animation

animate(animp, fps = 10, duration = 20, width = 800, height = 450) 

Save the Animation

anim_save(filename = "final_race_plot2.gif")

Lessons to Take Away

1. Do you need animation?

Prioritize the transmission of information effectively
Make your plot serve the audience, don’t be fancy if it’s not helpful

2. Make your animation awesome

Think carefully about transitions and speed

  • Figure out the in/out transitions that make sense and are not misleading/distracting
  • Give the eye enough time to understand each state

Get feedback and test your animation on naive viewers

Further Links/Reference

Thank you!

www.stephaniekirmer.com | @data_stephanie